// This is free and unencumbered software released into the public domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.

#ifndef KV_STDLIB_H
#define KV_STDLIB_H

#include "kianv_stdlib.h"
#include <stdarg.h>
#include <stdint.h>
extern long time();
extern long insn();

#if defined(NOT_USE_MYSTDLIB)
#else
extern char *malloc();
extern int printf(const char *format, ...);

extern void *memcpy(void *dest, const void *src, long n);
extern char *strcpy(char *dest, const char *src);
extern int strcmp(const char *s1, const char *s2);

char heap_memory[1024];
int heap_memory_used = 0;
#endif

static void printf_c(int c) { print_chr(c); }

static void printf_s(char *p) {
  while (*p)
    print_chr(*(p++));
}

static void printf_d(int val) {
  char buffer[32];
  char *p = buffer;
  if (val < 0) {
    printf_c('-');
    val = -val;
  }
  while (val || p == buffer) {
    *(p++) = '0' + val % 10;
    val = val / 10;
  }
  while (p != buffer)
    printf_c(*(--p));
}

static void printf_u(int val) {
  char buffer[32];
  char *p = buffer;

  val = val >= 0 ? val : -val;
  while (val || p == buffer) {
    *(p++) = '0' + val % 10;
    val = val / 10;
  }
  while (p != buffer)
    printf_c(*(--p));
}

int printf(const char *format, ...) {
  int i;
  va_list ap;

  va_start(ap, format);

  for (i = 0; format[i]; i++)
    if (format[i] == '%') {
      while (format[++i]) {
        if (format[i] == 'c') {
          printf_c(va_arg(ap, int));
          break;
        }
        if (format[i] == 's') {
          printf_s(va_arg(ap, char *));
          break;
        }
        if (format[i] == 'd') {
          printf_d(va_arg(ap, int));
          break;
        }
        if (format[i] == 'u') {
          printf_u(va_arg(ap, int));
          break;
        }
      }
    } else
      printf_c(format[i]);

  va_end(ap);
  return 0;
}

#if !defined(NOT_USE_MYSTDLIB)
char *malloc(int size) {
  char *p = heap_memory + heap_memory_used;
  // printf("[malloc(%d) -> %d (%d..%d)]", size, (int)p, heap_memory_used,
  // heap_memory_used + size);
  heap_memory_used += size;
  if (heap_memory_used > 1024)
    asm volatile("ebreak");
  return p;
}

void *memcpy(void *aa, const void *bb, long n) {
  // printf("**MEMCPY**\n");
  char *a = (char *)aa;
  const char *b = (const char *)bb;
  while (n--)
    *(a++) = *(b++);
  return aa;
}

char *strcpy(char *dst, const char *src) {
  char *r = dst;

  while ((((uint32_t)dst | (uint32_t)src) & 3) != 0) {
    char c = *(src++);
    *(dst++) = c;
    if (!c)
      return r;
  }

  while (1) {
    uint32_t v = *(uint32_t *)src;

    if (__builtin_expect((((v)-0x01010101UL) & ~(v)&0x80808080UL), 0)) {
      dst[0] = v & 0xff;
      if ((v & 0xff) == 0)
        return r;
      v = v >> 8;

      dst[1] = v & 0xff;
      if ((v & 0xff) == 0)
        return r;
      v = v >> 8;

      dst[2] = v & 0xff;
      if ((v & 0xff) == 0)
        return r;
      v = v >> 8;

      dst[3] = v & 0xff;
      return r;
    }

    *(uint32_t *)dst = v;
    src += 4;
    dst += 4;
  }
}

int strcmp(const char *s1, const char *s2) {
  while ((((uint32_t)s1 | (uint32_t)s2) & 3) != 0) {
    char c1 = *(s1++);
    char c2 = *(s2++);

    if (c1 != c2)
      return c1 < c2 ? -1 : +1;
    else if (!c1)
      return 0;
  }

  while (1) {
    uint32_t v1 = *(uint32_t *)s1;
    uint32_t v2 = *(uint32_t *)s2;

    if (__builtin_expect(v1 != v2, 0)) {
      char c1, c2;

      c1 = v1 & 0xff, c2 = v2 & 0xff;
      if (c1 != c2)
        return c1 < c2 ? -1 : +1;
      if (!c1)
        return 0;
      v1 = v1 >> 8, v2 = v2 >> 8;

      c1 = v1 & 0xff, c2 = v2 & 0xff;
      if (c1 != c2)
        return c1 < c2 ? -1 : +1;
      if (!c1)
        return 0;
      v1 = v1 >> 8, v2 = v2 >> 8;

      c1 = v1 & 0xff, c2 = v2 & 0xff;
      if (c1 != c2)
        return c1 < c2 ? -1 : +1;
      if (!c1)
        return 0;
      v1 = v1 >> 8, v2 = v2 >> 8;

      c1 = v1 & 0xff, c2 = v2 & 0xff;
      if (c1 != c2)
        return c1 < c2 ? -1 : +1;
      return 0;
    }

    if (__builtin_expect((((v1)-0x01010101UL) & ~(v1)&0x80808080UL), 0))
      return 0;

    s1 += 4;
    s2 += 4;
  }
}
#endif
#endif
